'''
	This module contains a wrapper for the vtkActor to make life easier in the
	view. 
	
	@author: J.H. Donker, J.Teeuw, S.G. Epskamp
	@copyright: Twente University
'''
import vtk
import wx
import math

from wx.lib.pubsub import Publisher
from util import XmlUtil



class HeaDSETVTKActor(vtk.vtkActor):
	'''
		A simple wrapper so we can keep track of geoms in the VTK components.
	'''
	
	def __init__(self):
		'''
			The constructor, doesn't do much
		'''
		self.__geom = None
	
	
		
	def SetGeom(self, geom):
		'''
			Associate a geom to the actor.

			@param geom: The geom friend of the actor.
		'''
		self.__geom = geom

	def GetGeom(self):
		'''
			Get the geom friend of this actor.
			
			@return: The geom friend of this actor.
		'''
		return self.__geom
	

class HeaDSETGeom:
	'''
		The HeadDSETGeom is the HeaDSET wraper for 3 dimensional objects in the
		system. In this implementation a geom is always a wrapper for a VTK
		actor. The class contains properties like scale, positon, rotation, 
		color, transparancy, name, boxwidgets, id and many others. Geom is short
		for geometry. 
		
		This really is a workhorse class. It contains a great number of 
		publisher subscriber messages. Most are meant for the output controllers
		since they are quite detailed. 

		The class also contains code to handle the boxwidget transformations, 
		this is done to keep this piece of isolated. Due to VTK design this 
		piece has some questionable links to the actor. 

		ID's are always unique to a session, but labels are not. 
	'''
	
	# default properties
	SCALE_FACTOR = 50.00;
	DEFSIZE = { 'length': 1.00 * SCALE_FACTOR, 'radius': 0.50 * SCALE_FACTOR, 'resolution': 50 }
	#DEFSIZE = { 'length': 1.00, 'radius': 0.50, 'resolution': 50 }
	
	# constants, for comparing against
	POSITION = "position"
	ORIENTATION = "orientation"
	SCALE = "scale"
	COLOR = "color"
	OPACITY = "opacity"
	LABEL = "label"
	
	SCALE_THRESHOLD = 0.10
	
	GEOM_ID = 1

	# name constants for labelling the different types of geoms
	BOX       = "Box"
	ELLIPSOID = "Ellipsoid"
	CYLINDER  = "Cylinder"
	CONE      = "Cone"
	DICOM     = "Dicom"
	
	# Constructor
	def __init__(self, actor, label):
		'''
			Creates a wrapping geom for an actor and gives it a label. This 
			constructor is meant as a workhorse constructor for the inheriting 
			classes. 
			
			@param actor: A vtk actor that will be wrapped by this geom. 
			@param label: A friendly name for the geom. 
		'''
		# set geom id
		self.__id = HeaDSETGeom.GEOM_ID
		HeaDSETGeom.GEOM_ID = HeaDSETGeom.GEOM_ID + 1
		
		# set properties
		self.__actor = actor
		self.__label = label
		
		self.__boxwidgets = []
		self.__bwenabled = False
		self.__widgetTransform = False
		self.__duringCallback = False
		self.__widgetTransforms = []
		
		self.__axes = vtk.vtkAxesActor()
		self.__axes.AxisLabelsOff()
		self.__axes.VisibilityOff()
		
		# back up properties
		self.__oldPosX, self.__oldPosY, self.__oldPosZ = self.__actor.GetPosition()
		self.__oldScaleX, self.__oldScaleY, self.__oldScaleZ = self.__actor.GetScale()
		self.__oldOrientationX, self.__oldOrientationY, self.__oldOrienationZ = self.__actor.GetOrientation()
		
		self.__oldColorRed, self.__oldColorGreen, self.__oldColorBlue = self.__actor.GetProperty().GetColor()
		self.__oldOpacity = -1
		self.__oldVisibility = self.__actor.GetVisibility()
		
		self.__oldLabel = self.__label
		
		self.__propChanged = False
		
		self.__instrument = False
		
		# set default properties
		#self.__actor.SetScale(self.SCALE_FACTOR, self.SCALE_FACTOR, self.SCALE_FACTOR)
		#self.SetOpacity(0.85) # NOTE: transparant by default
		
		# update view
		self.Update()
		#self.UpdateAxes()
		#self.UpdateBoxWidgets()
	
	
	######
	# Geom Parameter Procedures
	######
	
	def SetInstrument(self, value):
		'''
			Sets if a geom belongs to a instrument of not. 
			
			@param value: True if this is an instrument, False if this is not an
			              instrument.
		'''
		self.__instrument = value
	
	
	def IsInstrument(self):
		'''
			Answers the question if this is an instrument. 
			
			@return: True if this is an instrument, False if this is not an
			         instrument.
		'''
		return self.__instrument
	
	
	def GetID(self):
		'''
			Returns the ID of the geom.
			
			@return: The ID of the geom. 
		'''
		return self.__id
	
	
	def GetActor(self):
		'''
			Returns the VTK actor that this geom wraps. 
			
			@return: The actor of the geom. 
		'''
		return self.__actor
		
	
	def GetAxes(self):
		'''
			Returns the vtk axes actor for this geom.
			
			@return: The axes actor bound to this geom. 
		'''
		return self.__axes
	
	
	
	def SetPosition(self, xpos, ypos, zpos):
		'''
			Set this position of the geom in 3D space. The x, y, z arguments are
			in world cordinates. The method will automatically update the view
			components after applying the new cordinates. Thus causing direct
			feedback for the user of this class.
			
			@param xpos: The x component of the new position
			@param ypos: The y component of the new position
			@param zpos: The z component of the new position
			
		'''
		# back up old position
		self.__oldPosX, self.__oldPosY, self.__oldPosZ = self.__actor.GetPosition()
		
		# set the new position
		self.__actor.SetPosition(xpos, ypos, zpos)
		
		# trigger change events
		changed = False
		if not self.__oldPosX == xpos:
			
			# trigger event
			self.__sendMessage("PositionX")
			changed = True
		
		if not self.__oldPosY == ypos:
			
			# trigger event
			self.__sendMessage("PositionY")
			changed = True
		
		if not self.__oldPosZ == zpos:
			
			# trigger event
			self.__sendMessage("PositionZ")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Position")
			
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
	
	def GetPosition(self):
		'''
			Returns the position of the geom.
			
			@return: A tuple with the x,y,z cordinates of the geom. 
		'''
		return self.__actor.GetPosition()
		
		
	def SetScale(self, xscale, yscale, zscale):
		'''
			Sets the scale of the geom. This allows a geom to be scalled in
			three dimensions. All three dimensions are indepedant.
			 
			@param xscale: The x component of the new scale.
			@param yscale: The y component of the new scale.
			@param zscale: The z component of the new scale.

		'''
		
		# TEMP: reject negative scales
		if xscale < self.SCALE_THRESHOLD or yscale < self.SCALE_THRESHOLD or zscale < self.SCALE_THRESHOLD:
			return;
		
		# back up old scale
		self.__oldScaleX, self.__oldScaleY, self.__oldScaleZ = self.__actor.GetScale()
		
		# set the new scale
		self.__actor.SetScale(xscale, yscale, zscale)
		
		# trigger change events
		changed = False
		if not self.__oldScaleX == xscale:
			
			# trigger event
			self.__sendMessage("ScaleX")
			changed = True
		
		if not self.__oldScaleY == yscale:
			
			# trigger event
			self.__sendMessage("ScaleY")
			changed = True
		
		if not self.__oldScaleZ == zscale:
			
			# trigger event
			self.__sendMessage("ScaleZ")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Scale")
			
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
		
	def GetScale(self):
		'''
			Returns the scale of the geom.
			
			@return: A tupel with the x scale, y scale and z scale of the geom. 
		'''
		return self.__actor.GetScale()
		
		
	def SetOrientation(self, xorient, yorient, zorient):
		'''
			Sets the orientation of the geom. The geom can be rotated around 
			three local axis. (x,y,z) These axis are bound to the geom.
			
			@param xorient: The x component of the new orientation.
			@param yorient: The y component of the new orientation.
			@param zorient: The z component of the new orientation.
		'''
		# back up old orientation
		self.__oldOrientationX, self.__oldOrientationY, self.__oldOrientationZ = self.__actor.GetOrientation()
		
		# set the new orientation
		self.__actor.SetOrientation(xorient, yorient, zorient)
		
		# trigger change events
		changed = False
		if not self.__oldOrientationX == xorient:
			
			# trigger event
			self.__sendMessage("OrientationX")
			changed = True
		
		if not self.__oldOrientationY == yorient:
			
			# trigger event
			self.__sendMessage("OrientationY")
			changed = True
		
		if not self.__oldOrientationZ == zorient:
			
			# trigger event
			self.__sendMessage("OrientationZ")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Orientation")
			
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
	
	def GetOrientation(self):
		'''
			Returns the current orientation of the geom. The orientation that is
			returned may appear strange due to VTK behaviour. 
			
			@return: A tuple with the x,y,z components of the orienation
		'''
		return self.__actor.GetOrientation()
	
	
	def SetColor(self, red, green, blue):
		'''
			Sets the color of the geom. A color is made up of 3 components red,
			green and blue. 

			@param red:  The red component.
			@param green: The green component. 
			@param blue:
		'''
		# get property
		prop = self.__actor.GetProperty()
		
		# back up old color
		self.__oldColorR, self.__oldColorG, self.__oldColorB = prop.GetColor()
		
		# set the new color
		prop.SetColor(red, green, blue)
		
		# trigger change events
		changed = False
		if not self.__oldColorR == red:
			
			# trigger event
			self.__sendMessage("ColorR")
			changed = True
		
		if not self.__oldColorG == green:
			
			# trigger event
			self.__sendMessage("ColorG")
			changed = True
		
		if not self.__oldColorB == blue:
			
			# trigger event
			self.__sendMessage("ColorB")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Color")
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
	
	def GetColor(self):
		'''
			Returns the color component.
			@return: The color is returned in a tuple containing the red, green 
			         and blue component. 
		'''
		return self.__actor.GetProperty().GetColor()
		
		
	def SetOpacity(self, opacity):
		'''
			Set the opacity of the geom. (This may not work correctly with
			very complex geoms. Due to problems with VTK)
			
			@param opacity: The opacity ranges from 0 to 1. With 0 beinig 
			                totaly transparant and 1 beinig totaly opaque. 
		'''
		# get property
		prop = self.__actor.GetProperty()
		
		# back up old opacity
		self.__oldOpacity = prop.GetOpacity()
		
		# set the new opacity
		prop.SetOpacity(opacity)
			
		# trigger change events
		changed = False
		if not self.__oldOpacity == opacity:
			
			# trigger event
			#self.__sendMessage("Opacity")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Opacity")
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
	
	
	def GetOpacity(self):
		'''
			Returns the opacity of the geom.
			
			@return: The opacity of the geom in a value between 0 and 1. 
		'''
		return self.__actor.GetProperty().GetOpacity()
			
	
	def SetVisibile(self):
		'''
			Always makes the geom visible. 
		'''
		# change the visibility
		self.SetVisibility(True)
			

	def SetInvisibile(self):
		'''
			Always makes the geom invisble. 
		'''
		# change the visibility
		self.SetVisibility(False)
			
	
	def SetVisibility(self, flag):
		'''
			Changes the visibility of the geom depending on the argument given.
			
			@param flag: The desired vislity for the geom. 
		'''
		# back up old visibility
		self.__oldVisibility = self.__actor.GetVisibility()
		
		# set the new visibility
		if flag:
			
			self.__actor.VisibilityOn()
			#self.__axes.VisibilityOn()
		
		else:
			
			self.__actor.VisibilityOff()
			self.__axes.VisibilityOff()
			self.DisableBoxWidgets() # NOTE: disable / deselect geom
			
		# trigger change events
		changed = False
		if not (self.__oldVisibility == flag):
			
			# trigger event
			#self.__sendMessage("GeomVisibility")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Visibility")
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
	
	# get the visibility of the geom
	def GetVisibility(self):
		'''
			Gives the current visibility of the geoms. 
			
			@return: True if visible, false if invisible.
		'''
		return self.__actor.GetVisibility()
		
	
	# set the label of the geom
	def SetLabel(self, label):
		'''
			Changes the label of the geom to the desired name.
			
			@param label: The desired label for the geom.
		'''
		# back up old label
		self.__oldLabel = self.__label
		
		# set the new label
		self.__label = label
				
		# trigger change events
		changed = False
		if not self.__oldLabel == label:
			
			# trigger event
			#self.__sendMessage("GeomLabel")
			changed = True
		
		if changed:
			
			# trigger event
			self.__sendMessage("Label")
			self.__propChanged = True
		
		# update actor, boxwidgets, axes
		self.Update()
		
	
	# get the label of the geom
	def GetLabel(self):
		'''
			Returns the label of the geom.
			
			@return: The label of the geom. 
		'''
		return self.__label


	#####
	# Cloning Procedures
	#####
	
	# private method for funky cloning
	def TypeCopy(self):
		'''
			This method is used during cloning to get a copy of the type. This
			method is meant to be overriden. 
			
			@return: None if this geom is uncloneable. A fresh geom of the same
			         type if the geom is cloneabe. 
		'''
		return None
		
	
	# gives back a deep copy of the geom
	def Clone(self):
		'''
			Clones the geom. This means the geom will be of the same class,
			in the same collection and will share all properties with this geom.
			
			Cloning works with a deep copy. 
			
			@return: A clone og this geom. 
		'''
		
		return self.TypeCopy()
		
		"""
		cloned = self.TypeCopy()
		#print self, cloned

		x,y,z = self.GetOrientation()
		cloned.SetOrientation(x,y,z)
				

		x,y,z = self.GetPosition()
		cloned.SetPosition(x,y,z)

		# update render views
		#self.__sendMessage("RenderUpdate")

		x,y,z = self.GetScale()
		cloned.SetScale(x,y,z)		

		#disabled since we can only cloned picked objects, this is nonesense
		#o = self.GetOpacity()
		#cloned.SetOpacity(o)
		
		#r,g,b = self.GetColor()
		#cloned.SetColor(r,g,b)
		
		v = self.GetVisibility()
		cloned.SetVisibility(v)
		#print "Clone"
		return cloned
		"""
		
	#####
	# Boxwidget controls
	#####
	
	def AddBoxWidget(self, boxwidget):
		'''
			Adds a new boxwidget to the list of boxwidgets.
			
			@param boxwidget: Boxwidget to be added to the list.
		'''
		self.__boxwidgets.append(boxwidget)
		
		if self.__bwenabled:
			
			# enable box widget immediately
			boxwidget.On()
		
		else:
			
			# disable box widget
			boxwidget.Off()
	
	
	def EnableBoxWidgets(self):
		'''
			Enables the boxwidgets that are assigned to this geom. This allows
			for interaction in the VTK components. 
		'''
		if not self.__bwenabled:
		
			# set enabled flag
			self.__bwenabled = True
			
			# backup opacity
			self.__oldOpacity = self.GetOpacity()
				
			# set new opacity
			self.__actor.GetProperty().SetOpacity(0.50)
			
			# enable the box widgets
			for boxwidget in self.__boxwidgets:
				
				# enable the box widget
				boxwidget.On()
			
			# enable the axes
			self.__axes.VisibilityOn()
			
			# update view
			self.Update()
	
	
	def DisableBoxWidgets(self):
		'''
			Disables the boxwidgets assigned to the geom. Thus disabling the 
			user interaction with the boxwidget. 
		'''
		if self.__bwenabled:
			
			# set enabled flag
			self.__bwenabled = False
			
			# restore opacity
			self.__actor.GetProperty().SetOpacity(self.__oldOpacity)
			
			# disable the box widgets
			for boxwidget in self.__boxwidgets:
				
				# disable the box widget
				boxwidget.Off()
			
			# disable the axes
			self.__axes.VisibilityOff()
			
			# update view
			self.Update()
	
	
	# private callback to update the geom according to the transformation of the box widget
	def _StartInteractionCallback(self, widget, eventString):
		'''
			This 
		'''
		# start widget transform
		#self.__widgetTransform = True
		
		# reset list of transforms
		self.__widgetTransforms = []
	
	
	# private callback to update the geom according to the transformation of the box widget
	def _InteractionCallback(self, widget, eventString):
		'''
			Private callback used to handle the boxwidget transforms. 
			
			@attention: This is a private callback! 
		'''
		
		# set during 
		self.__duringCallback = True
		
		# get transform
		t = vtk.vtkTransform()
		widget.GetTransform(t)
		
		# get the transformation parameters
		xpos, ypos , zpos = t.GetPosition()
		xscale, yscale, zscale = t.GetScale()
		xorient, yorient, zorient = t.GetOrientation()
			
		"""
		# restrict transformation
		correction = False
		invert = False
		
		if xscale < 0.00 and yscale < 0.00 and zscale < 0.00:
			# invert
			invert = True
			xscale = -1 * xscale
			yscale = -1 * yscale
			zscale = -1 * zscale
		
		# NOTE: additional correction seems neccesary!!
		if xscale < self.SCALE_THRESHOLD:
			correction = True
			xscale = self.SCALE_THRESHOLD + 0.1
		
		# NOTE: additional correction is neccesary!!
		if yscale < self.SCALE_THRESHOLD:
			correction = True
			yscale = self.SCALE_THRESHOLD + 0.1
			
		# NOTE: additional correction is neccesary!!
		if zscale < self.SCALE_THRESHOLD:
			correction = True
			zscale = self.SCALE_THRESHOLD + 0.1
		"""
		# public access
		###if not correction:
		
		
		# TEMP: reject negative scales
		correction = False
		
		if xscale < self.SCALE_THRESHOLD:
			xscale = self.SCALE_THRESHOLD
			correction = True
		
		if yscale < self.SCALE_THRESHOLD:
			yscale = self.SCALE_THRESHOLD
			correction = True
		
		if zscale < self.SCALE_THRESHOLD:
			zscale = self.SCALE_THRESHOLD
			correction = True
			
		if correction:
			self.UpdateBoxWidgets() # NOTE: prevents the box widget from scaling negative
		else:
			self.SetPosition(xpos, ypos, zpos)
			self.SetScale(xscale, yscale, zscale)
			self.SetOrientation(xorient, yorient, zorient)
			
		# private access
		###if not correction:
		#	self.__actor.SetPosition(xpos, ypos, zpos)
		#
		#self.__actor.SetScale(xscale, yscale, zscale)
		#self.__actor.SetOrientation(xorient, yorient, zorient)
		
		# unset during callback
		self.__duringCallback = False
		
		# force update
		#self.__propChanged = True (only if properties changed! [public access only])
		
		# update the view
		self.Update()
	
	
	# private callback to update the geom according to the transformation of the box widget
	def _EndInteractionCallback(self, widget, eventString):
		'''
			Private callback to update the geom according to the transformation 
			of the box widget.
			
			@attention: This is a private callback!
		'''
		# stop widget transform
		#self.__widgetTransform = False
	
		# force update
		#self.__propChanged = True

		# update
		#self.Update()
		
		# send last of most recent changes
		for msg in set(self.__widgetTransforms):
			#print("Resending message: " + msg)
			Publisher.sendMessage(msg, self)
		
	
	#####
	# Update Procedures
	#####
	
	# update the axes and the box widgets accoring to the transformation of the geom
	def Update(self):
		'''
			Updates that something has changed to all fans and subscribers of 
			this class. This includes renders, gui's, boxwidgets and axes.
		'''
		# during box widget transformation
		#if self.__duringCallback:
		#	
		#	# update axes
		#	self.UpdateAxes()
		#
		#if self.__propChanged and not self.__widgetTransform:# and not self.__duringCallback:
		if self.__propChanged:
		
			# update view
			self.UpdateAxes()
			self.UpdateBoxWidgets()
		
			# trigger event
			self.__sendMessage("Updated")
			
			# unset properties changed flag
			self.__propChanged = False
	
			
	# update the axes according to the transformation of the geom
	def UpdateAxes(self):
		'''
			Updates the axes to match the current dimensions of the geom. 
		'''	
		# get the transformation parameters
		xpos, ypos , zpos = self.GetPosition()
		xscale, yscale, zscale = self.GetScale()
		xorient, yorient, zorient = self.GetOrientation()
		
		# tranform axes (without scaling)
		axesTransform = vtk.vtkTransform()
		axesTransform.Translate(xpos, ypos, zpos)
		axesTransform.RotateZ(zorient)
		axesTransform.RotateX(xorient)
		axesTransform.RotateY(yorient)
		axesTransform.Scale(1.00, 1.00, 1.00) # NOTE: do NOT apply scaling!!
		
		self.__axes.SetUserTransform(axesTransform)
		
		# scale axes manually
		xsf = 0.5 * xscale + ((0.3 * xscale) / xscale)
		ysf = 0.5 * yscale + ((0.3 * yscale) / yscale)
		zsf = 0.5 * zscale + ((0.3 * zscale) / zscale)
		
		self.__axes.SetTotalLength(xsf * self.SCALE_FACTOR, ysf * self.SCALE_FACTOR, zsf * self.SCALE_FACTOR)
		
		# set proportion of tips
		ntx = math.fabs(0.20 / xsf)
		nty = math.fabs(0.20 / ysf)
		ntz = math.fabs(0.20 / zsf)
		
		if ntx > 1.00: ntx = 1.00
		if nty > 1.00: nty = 1.00
		if ntz > 1.00: ntz = 1.00
		
		self.__axes.SetNormalizedTipLength(ntx, nty, ntz)
		self.__axes.SetNormalizedShaftLength(1 - ntx, 1 - nty, 1 - ntz)
		
		# force an update on the view
		#self.__sendMessage("Update")
			
	
	def UpdateBoxWidgets(self):
		'''
			Updates the boxwidgets so they are close to the geom. 
		'''
		# get the transformation parameters
		xpos, ypos , zpos = self.GetPosition()
		xscale, yscale, zscale = self.GetScale()
		xorient, yorient, zorient = self.GetOrientation()
		
		# transform box widget
		boxTransform = vtk.vtkTransform()
		boxTransform.Translate(xpos, ypos, zpos)
		boxTransform.RotateZ(zorient)
		boxTransform.RotateX(xorient)
		boxTransform.RotateY(yorient)
		boxTransform.Scale(xscale, yscale, zscale)
		
		# transform all box widgets
		for bw in self.__boxwidgets:
		
			# transform box widget
			bw.SetTransform(boxTransform)
		
		# force an update on the view
		#self.__sendMessage("Update")
	
	
	def __sendMessage(self, message):
		'''
			Private method to send message in a wrapped way.
		'''

		# add type to message
		if self.__instrument:
			message = "Instrument" + message
		else:
			message = "Geom" + message

		# handle message
		if self.__duringCallback:
			
			# store message
			#print("Storing message: " + message)
			self.__widgetTransforms.append(message)# = self.__widgetTransforms | set([message])
			
		else:
		
			# forward message
			#print("Sending message: " + message)
			Publisher.sendMessage(message, self)
	
	
	#####
	# Save Geom Procedures
	#####
	
	def Save(self, doc):
		'''
			Creates a tree of dom nodes that represent this geom.
			
			@param doc: A DOM document
			@return: The DOM tree that represents this Geom
		'''
		# create top-level node
		root = doc.createElement(self.__class__.__name__)
		
		# create nodes for all settings with values on the XYZ axes, such as scale
		settings = (self.GetPosition(), self.GetScale(), self.GetOrientation())
		settingNames = (self.POSITION, self.SCALE, self.ORIENTATION)
		# iterate over all settings
		for settingName, setting in zip(settingNames, settings):
			# curNode = top node for current setting
			curNode = root.appendChild(doc.createElement(settingName));

			# save values for all axes of all settings
			XmlUtil.SetAttributes(curNode, ('x','y','z'), setting)
		
		# save color
		curNode = root.appendChild(doc.createElement(self.COLOR));
		XmlUtil.SetAttributes(curNode, ('r','g','b'), self.GetColor())

		# save settings with single value
		settingNames = (self.OPACITY, self.LABEL)
		settingValues = (self.GetOpacity(), self.GetLabel())
		XmlUtil.SetAttributes(root, settingNames, settingValues)
			
		return root
	
